/*
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.orm.factory;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cc.aileron.accessor.PojoAccessor;
import cc.aileron.accessor.PojoAccessorManager;
import cc.aileron.accessor.PojoAccessorValueNotFoundException;
import cc.aileron.accessor.PojoPropertiesNotFoundException;
import cc.aileron.commons.instance.InstanceManager;
import cc.aileron.commons.resource.ResourceNotFoundException;
import cc.aileron.orm.logger.SqlLogger;
import cc.aileron.orm.logger.SqlLoggerParameter;
import cc.aileron.orm.sql.G2DaoDto;
import cc.aileron.orm.sql.G2DaoSqlMap;
import cc.aileron.template.Template;
import cc.aileron.template.flow.FlowMethodNotFoundError;

/**
 * @author Aileron
 * @param <T>
 */
public class PreparedStatementFactoryImpl<T> implements
    PreparedStatementFactory<T>
{
    @Override
    public PreparedStatement create(final String name,
            final Connection connection,
            final Object pojo) throws SQLException,
            FlowMethodNotFoundError,
            PojoAccessorValueNotFoundException,
            PojoPropertiesNotFoundException
    {
        final G2DaoDto dto = new G2DaoDto(pojo);
        final PojoAccessor<G2DaoDto> accessor = accessorFactory.from(dto);
        final StringWriter stringWriter = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(stringWriter);

        final Template template = sqlMap.get(targetClass, name);
        if (template == null)
        {
            throw new Error(new ResourceNotFoundException(targetClass + "@"
                    + name));
        }

        if (logger.isTraceEnabled())
        {
            final String templateString = template.toString();
            logger.trace(targetClass.getName() + "@" + name + "=>"
                    + templateString);
        }

        template.print(accessor, printWriter);
        printWriter.flush();
        printWriter.close();

        final String sql = stringWriter.toString();

        if (isDebug)
        {
            final SqlLoggerParameter parameter = new SqlLoggerParameter(
                    targetClass.getName() + "#" + name, sql, dto.parameters);
            sqlLogger.output(parameter);
        }

        final PreparedStatement preparedStatement = connection.prepareStatement(
                sql, ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_UPDATABLE);
        for (int max = dto.parameters.size(), i = 0; i < max; i++)
        {
            final Object parameter = dto.parameters.get(i);
            preparedStatement.setObject(i + 1,
                    typeConvertManager.convert(parameter));
        }
        return preparedStatement;
    }

    /**
     * @param targetClass
     * @param sqlMap
     * @param provider
     */
    public PreparedStatementFactoryImpl(
        final Class<T> targetClass,
        final G2DaoSqlMap sqlMap,
        final InstanceManager provider)
    {
        this.targetClass = targetClass;
        this.accessorFactory = provider.get(PojoAccessorManager.class);
        this.sqlMap = sqlMap;
        this.sqlLogger = provider.get(SqlLogger.class);
        this.isDebug = sqlLogger.isEnabled();
        this.typeConvertManager = provider.get(G2DaoParameterConvertManager.class);
    }

    private final PojoAccessorManager accessorFactory;

    private final boolean isDebug;

    private final Logger logger = LoggerFactory.getLogger(this.getClass()
        .getName());

    private final SqlLogger sqlLogger;

    private final G2DaoSqlMap sqlMap;

    private final Class<T> targetClass;

    private final G2DaoParameterConvertManager typeConvertManager;
}
